#include "Rlinda.h"

linda_space* linda_create(char *path){
    char buf[MAX_PATH];
    FILE *f;
    
    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // na wszelki wypadek
    
    if (getcwd(buf, MAX_PATH) == NULL)
    {
      return NULL;
    }
    
    if (chdir(path) == -1)
    {
      return NULL;
    }

    linda_space* new_space = (linda_space*)malloc(sizeof(linda_space));
    new_space->path = path;
    
    _linda_create_rw_lock("m_lock", &(new_space->lock));

    mkdir("take_pattern", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    mkdir("leave_pattern", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    mkdir("tuple", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

    f = fopen("seq", "w");
    if (f == NULL)
    {
      chdir(buf);
      return NULL;
    }
    fprintf(f, "%d", 1);
    fclose(f);
    
    chdir(buf);
    return new_space;
}

linda_space* linda_init(char *path){
    char buf[MAX_PATH];
    
    if (getcwd(buf, MAX_PATH) == NULL)
    {
      return NULL;
    }
    
    if (chdir(path) == -1)
    {
      return NULL;
    }

    linda_space* new_space = (linda_space*)malloc(sizeof(linda_space));
    new_space->path = path;
    
    _linda_get_rw_lock("m_lock", &(new_space->lock));
    
    chdir(buf);
    return new_space;
}

void linda_close(linda_space * linda){
  _linda_leave_rw_lock(&(linda->lock));
  free(linda);
}

void linda_destroy(linda_space * linda)
{
  char line[1024];
  
  _linda_destroy_rw_lock(&(linda->lock));
  sprintf(line, "rm -r %s/*", linda->path);
  system(line);
  free(linda);
}

unsigned int seq(linda_space* space)
{
    char buf[MAX_PATH];
    char* path = space->path;
    FILE *f = NULL;
    unsigned int rseq;
    
    if (getcwd(buf, MAX_PATH) == NULL)
    {
      return 0;
    }
    
    if (chdir(path) == -1)
    {
      printf("!: %s !!! %s\n", path, buf);
      return 0;
    }
    
    f = fopen("seq", "r");
    if (f == NULL)
    {
      chdir(buf);
      return 0;
    }
    fscanf(f, "%d", &rseq);
    fclose(f);
    f = fopen("seq", "w");
    if (f == NULL)
    {
      chdir(buf);
      return 0;
    }
    fprintf(f, "%u", rseq+1);
    fclose(f);
    chdir(buf);
    return rseq;
}

char* seqstr(linda_space* space)
{
  char *ret = malloc(20*sizeof(char));
  snprintf(ret, 20, "%u", seq(space));
  return ret;
}

void linda_output(linda_space * linda,const tuple * t)
{
  char *fn;
  _linda_rw_lock *lock = &(linda->lock);
  _linda_rw_condition cond;
  pattern * p;
  char buf[MAX_PATH], path2best[MAX_PATH] = { 0 } ;
  pid_t rpid, spid;
  
  DIR *dir;
  struct dirent *de;
  // Na poczatek blokada
  _linda_lock_writer(&(linda->lock));
  // Zapamietaj katalog roboczy
  getcwd(buf, MAX_PATH);
  fn = seqstr(linda);
  chdir(linda->path);
  chdir("tuple");
  write_tuple(fn, t);
  chdir("../leave_pattern");
  // Obudz wszystkie odczyty nieniszczace
  dir = opendir(".");
  while ((de = readdir(dir)) != NULL)
  {
    if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
    p = read_pattern(de->d_name, NULL);
    if (comparison(p, t))
    {
      _linda_get_rw_condition(lock, de->d_name, &cond);
      _linda_writer_signal_condition(&cond);
    }
    free(p);
  }
  closedir(dir);
  // Znajdz najlepszy odczyt niszczacy
  chdir("../take_pattern");
  dir = opendir(".");
  while ((de = readdir(dir)) != NULL)
  {
    if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
    p = read_pattern(de->d_name, &rpid);
    if (comparison(p, t))
    {
      if (path2best[0] == 0)
      {
	spid = rpid;
	strcpy(path2best, de->d_name);
      }
      else if (_get_last_pid(linda) != rpid) {
	spid = rpid;
	strcpy(path2best, de->d_name);
      }
    }
    free(p);
  }
  closedir(dir);
  // Obudz odczyt niszczacy, o ile taki byl
  if (path2best[0] != 0)
  {
    _set_last_pid(linda, spid);
    _linda_get_rw_condition(lock, path2best, &cond);
    _linda_writer_signal_condition(&cond);
  }
  // Zakoncz
  chdir(buf);
  _linda_release_writer(lock);
}

tuple* linda_input(linda_space * linda,pattern* p ,int timeout)
{
  char *fn = NULL, buf[80], path2tuple[MAX_PATH] = { 0 };
  DIR *dir;
  struct dirent *de;
  tuple *t = NULL;
  
  _linda_rw_lock *lock = &(linda->lock);
  _linda_rw_condition cond;
  
  // Blokada pisarza (czytelnik nie ma WIEKSZEGO sensu)
  _linda_lock_writer(lock);
  fn = seqstr(linda);
  // Zapamietaj katalog roboczy
  getcwd(buf, MAX_PATH);
  chdir(linda->path);
  // Iteruj po katalogu z krotkami
  chdir("tuple");
  dir = opendir(".");
  while ((de = readdir(dir)) != NULL)
  {
    if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
    t = read_tuple(de->d_name);
    if (comparison(p, t))
    {
      strcpy(path2tuple, de->d_name);
      break;
    }
    free(t);
    t = NULL;
  }
  closedir(dir);
  if (t != NULL)
  {
    // Jest krotka!
    unlink(path2tuple);
    chdir("..");
    chdir(buf);
    free(fn);
    _linda_release_writer(lock);
    return t;
  } else {
    // Nie ma krotki. Ustaw pattern
    chdir("../take_pattern");
    write_pattern(fn, p);
    _linda_create_rw_condition(lock, fn, &cond);
    _linda_writer_wait_condition(&cond, timeout);
    // ...
    // Cos nas obudzilo, nie wiemy co
    // Czy jest poszukiwana krotka?
    chdir("../tuple");
    dir = opendir(".");
    while ((de = readdir(dir)) != NULL)
    {
      if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
      t = read_tuple(de->d_name);
      if (comparison(p, t))
      {
	strcpy(path2tuple, de->d_name);
	break;
      }
      free(t);
      t = NULL;
    }
    // Jest krotka albo nie ma - ale jak nie ma to timeout
    closedir(dir);
    chdir("../take_pattern");
    unlink(fn);
    chdir("../tuple");
    unlink(path2tuple);
    chdir(buf);
    free(fn);
    _linda_release_writer(lock);
    return t;
    }
}

tuple* linda_read(linda_space * linda,pattern* p ,int timeout)
{
  char *fn = NULL, buf[80], path2tuple[MAX_PATH] = { 0 };
  DIR *dir;
  struct dirent *de;
  tuple *t = NULL;
  
  _linda_rw_lock *lock = &(linda->lock);
  _linda_rw_condition cond;
  
  // Blokada czytelnika
  _linda_lock_reader(lock);
  // Zapamietaj katalog roboczy
  getcwd(buf, MAX_PATH);
  chdir(linda->path);
  // Iteruj po katalogu z krotkami
  chdir("tuple");
  dir = opendir(".");
  while ((de = readdir(dir)) != NULL)
  {
    if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
    t = read_tuple(de->d_name);
    if (comparison(p, t))
    {
      strcpy(path2tuple, de->d_name);
      break;
    }
    free(t);
    t = NULL;
  }
  closedir(dir);
  if (t != NULL)
  {
    // Jest krotka!
    chdir(buf);
    _linda_release_reader(lock);
    return t;
  } else {
    // Nie ma krotki. Ustaw pattern
    _linda_release_reader(lock);
    _linda_lock_writer(lock);
    chdir(buf);
    fn = seqstr(linda);
    chdir(linda->path);
    chdir("tuple");
    // Nie wiemy co dzialo sie w miedzyczasie - trzeba jeszcze raz przeskanowac przestrzen
    dir = opendir(".");
    while ((de = readdir(dir)) != NULL)
    {
      if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
      t = read_tuple(de->d_name);
      if (comparison(p, t))
      {
	strcpy(path2tuple, de->d_name);
	break;
      }
      free(t);
      t = NULL;
    }
    closedir(dir);
    if (t == NULL)
    {
      chdir("../take_pattern");
      write_pattern(fn, p);
      _linda_create_rw_condition(lock, fn, &cond);
      _linda_writer_wait_condition(&cond, timeout);
      // ...
      // Cos nas obudzilo, nie wiemy co
      // Czy jest poszukiwana krotka?
      chdir("../tuple");
      dir = opendir(".");
      while ((de = readdir(dir)) != NULL)
      {
	if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue;
	t = read_tuple(de->d_name);
	if (comparison(p, t))
	{
	  strcpy(path2tuple, de->d_name);
	  break;
	}
	free(t);
	t = NULL;
      }
      closedir(dir);
      chdir("../take_pattern");
      unlink(fn);
    }
    // Jest krotka albo nie ma - ale jak nie ma to timeout
    chdir(buf);
    free(fn);
    _linda_release_writer(lock);
    return t;
    }
}

pid_t _get_last_pid(linda_space* space)
{
    char buf[MAX_PATH];
    char* path = space->path;
    FILE *f = NULL;
    pid_t rpid;
    
    if (getcwd(buf, MAX_PATH) == NULL)
    {
      return 0;
    }
    
    chdir("..");
    
    f = fopen("last_pid", "r");
    if (f == NULL)
    {
      chdir(buf);
      return 0;
    }
    fscanf(f, "%u", &rpid);
    fclose(f);
    chdir(buf);
    return rpid;  
}

void _set_last_pid(linda_space* space, pid_t pid)
{
    char buf[MAX_PATH];
    char* path = space->path;
    FILE *f = NULL;
    unsigned int rseq;
    
    if (getcwd(buf, MAX_PATH) == NULL)
    {
      return;
    }
    
    chdir("..");
    
    f = fopen("last_pid", "w");
    if (f == NULL)
    {
      chdir(buf);
      return;
    }
    fprintf(f, "%u", pid);
    fclose(f);
    chdir(buf);
}